package contatos.collection;

import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.TreeSet;
import contatos.model.Contato;
import contatos.patterns.observer.Observer;
import contatos.patterns.observer.Subject;

/**
 * @author felipe
 */
public final class Contatos implements Subject {
    
    private static Contatos instancia;
    private ArrayList<Observer> observers;
    private TreeSet<Contato> contatos = null;
    private final File arquivo;
    
    private Contatos() throws Exception {
        //verificacao da instancia, padrao singleton
        if (instancia != null) {
            throw new IllegalStateException("Instância já existente!");
        }

        //lista de observadores, padrao observer
        observers = new ArrayList<Observer>();

        //treeset de contatos
        contatos = new TreeSet<Contato>();

        //arquivo de persistencia
        arquivo = new File("src/contatos/data/contatos.data");
        
        carregaPessoas();
        
    }

    //metodo para retornar a instancia (Singleton)
    public static Contatos getInstancia() throws Exception {
        if (instancia == null) {
            instancia = new Contatos();
        }
        return instancia;
    }
    
    //metodo pra ler o arquivo e carregar o treeset de contatos
    public void carregaPessoas() throws Exception {
        try {
            FileInputStream fs = new FileInputStream(arquivo);
            ObjectInputStream in = new ObjectInputStream(fs);
            contatos = (TreeSet) in.readObject();
        } catch (EOFException e) {
            Persistir(true);
        } catch (FileNotFoundException e) {
            try {
                arquivo.createNewFile();
            } catch (IOException ex) {
                throw new Exception("Não foi possível criar o arquivo de dados");
            }
        } catch (IOException ex) {
            throw new Exception("Não foi possível ler o arquivo de dados");
        }
    }
    
    public boolean incluir(Contato p) throws Exception {
        boolean add = this.contatos.add(p);
        Persistir(add);
        return add;
    }
    
    public boolean alterar(Contato contato, String nome, String telefone, String operadora, String uf) throws Exception {
        contato.setNome(nome);
        contato.setTelefone(telefone);
        contato.setOperadora(operadora);
        contato.setUf(uf);
        Persistir(true);
        return true;
    }
    
    public boolean excluir(Contato p) throws Exception {
        boolean remove = this.contatos.remove(p);
        Persistir(remove);
        return remove;
    }
    
    public void addAll(Collection ps) {
        this.contatos.addAll(ps);
    }
    
    public boolean contains(Contato p) {
        return this.contatos.contains(p);
    }
    
    public TreeSet<Contato> getTreeSet() {
        return contatos;
    }
    
    public Iterator<Contato> iterator() {
        return this.contatos.iterator();
    }
    
    public void Persistir(boolean persistance) throws Exception {
        if (persistance) {
            notifyObservers();
            try {
                FileOutputStream fo = new FileOutputStream(arquivo);
                ObjectOutputStream oo = new ObjectOutputStream(fo);
                oo.writeObject(contatos);
            } catch (FileNotFoundException ex) {
                try {
                    arquivo.createNewFile();
                    Persistir(true);
                } catch (IOException ex1) {
                    throw new Exception("O arquivo de persistencia não foi encontrado e não foi possível cria-lo");
                }
            } catch (IOException ex) {
                throw new Exception("Não foi possível persistir os dados");
            }
        }
    }
    
    public TreeSet<Contato> getCompletos(){
        TreeSet<Contato> completos = new TreeSet<Contato>();
        for(Contato contato : contatos){
            if(!contato.getOperadora().equals("") && !contato.getUf().equals("")){
                completos.add(contato);
            }
        }
        return completos;
    }
    
    @Override
    public void registryObserver(Observer o) {
        observers.add(o);
    }
    
    @Override
    public void removeObserver(Observer o) {
        observers.remove(o);
    }
    
    @Override
    public void notifyObservers() {
        for (Observer obs : observers) {
            obs.update(this);
        }
    }
}
